#include "xcom_parser.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <utils/logger.h>
#include <stack>
#include <regex>

namespace protocol {
bool XComParser::LoadXComFile(const std::string &filePath)
{
	std::ifstream xcomFile(filePath);
	if (!xcomFile.is_open()) {
		HTELINK_LOG_ERR("%s open failed", filePath.c_str());
		return false;
	}

	std::string line;
	while (std::getline(xcomFile, line)) {
		line = utils::trim(line);
		if (ParseLine(line) == false) {
			return false;
		}
	}
	xcomFile.close();
	return true;
}

bool XComParser::ParseLine(const std::string &line)
{
	if (!stackNode_.empty() && stackNode_.top() == "```") {
		if (!utils::StartWith(line, "```")) {
			stackKVs_.push_back(line);
			return true;
		}
	}

	int pos = 0;
	std::string token;
	while (pos < line.length()) {
		token = WrapToken(line, pos);
		if (token == "#") {
			// comment
			break;
		} else if (token == "{") {
			stackNode_.push("{");
		} else if (token == "}") {
			if (stackNode_.empty()) {
				HTELINK_LOG_ERR("format error, too more }");
				return false;
			}
			if (stackNode_.top() != "{") {
				HTELINK_LOG_ERR("format error, no { found to match }");
				return false;
			}
			stackNode_.pop();
			if (stackNode_.empty() || stackNode_.top() != ":") {
				continue;
			}
			stackNode_.pop();
			stackKey_.pop_back();
		} else if (token == "```") {
			if (stackNode_.top() == "```") {
				stackNode_.pop();
				ParseTemplate();
				continue; // 退出
			}
			stackNode_.push("```");
			break;
		} else if (token == ":") {
			stackNode_.push(":");
		} else if (token == "," || token == "") {
		} else {
			if (stackNode_.empty()) {
				stackKey_.push_back(token);
				continue;
			}
			if (stackNode_.top() == "{") {
				stackKey_.push_back(token);
			} else if (stackNode_.top() == ":") {
				stackNode_.pop();
				if (stackKey_.empty()) {
					HTELINK_LOG_ERR("key from key:value pair is loss");
					return false;
				}
				std::string key = stackKey_.back();
				std::string value = token;
				stackKey_.pop_back();
				if (!ParseKV(key, value)) {
					return false;
				}
			} else {
				HTELINK_LOG_ERR("not reconize %s", stackNode_.top().c_str());
				return false;
			}
		}
	}
	return true;
}
std::string XComParser::WrapToken(const std::string &line, int32_t &pos)
{
	std::string token;
	for (int i = pos; i < line.length(); i++) {
		const char &ch = line[i];
		if (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == '#') {
			pos = i;
			if (token.empty()) {
				token = ch;
				pos = ++i;
				return token;
			}
			return token;
		} else if (ch == ' ' || ch == '\t') {
			if (!token.empty()) {
				pos = ++i;
				return token;
			}
		} else if (ch == '`') {
			if (!token.empty()) {
				if (token.back() != '`') {
					pos = ++i;
					return token;
				}
			}
			token += '`';
			if (token == "```") {
				pos = ++i;
				return token;
			}
		} else if (ch == '(') {
			pos = i;
			std::string corsp = GetCorrespStr(line, pos, {'(', ')'});
			token += corsp;
			i = pos;
		} else if (ch == '[') {
			pos = i;
			std::string corsp = GetCorrespStr(line, pos, {'[', ']'});
			token += corsp;
			i = pos;
		} else {
			token += ch;
			pos = i;
		}
	}
	++pos;
	return token;
}

void XComParser::Clear() {
	while (!stackNode_.empty()) {
		stackNode_.pop();
	}
	stackKey_.clear();
	stackKVs_.clear();
	mapXComNodes_.clear();
}


bool XComParser::ParseKV(const std::string &key, const std::string &value)
{
	if (stackKey_.empty()) {
		return true;
	}
	if (mapXComNodes_.find(stackKey_[0]) == mapXComNodes_.end()) {
		mapXComNodes_[stackKey_[0]] = std::make_shared<XComNode>();
		mapXComNodes_[stackKey_[0]]->SetId(stackKey_[0]);
	}
	std::shared_ptr<XComNode> xcomNode = mapXComNodes_[stackKey_[0]];
	if (stackKey_.size() == 1) {
		if (key != "name") {
			HTELINK_LOG_ERR("%s does not support %s field, ignore it",
				stackKey_.back().c_str(), key.c_str());
			return true;
		}
		xcomNode->SetName(value);
		return true;
	} else if (stackKey_.size() == 2) {
		if (stackKey_.back() == "protocol") {

		} else if (stackKey_.back() == "funcpoints") {
		}
	} else if (stackKey_.size() == 3) {
		if (stackKey_[1] == "protocol") {
			std::string flow = stackKey_[2];
			xcomNode->AddFlowExpr(flow, key, value);
			return true;
		} else if (stackKey_[1] == "funcpoints") {
			std::string funcpoint = stackKey_[2];
			xcomNode->AddFuncPointVar(funcpoint, key, value);
			return true;
		} else {
			HTELINK_LOG_ERR("%s does not support %s field, ignore it",
				stackKey_.back().c_str(), key.c_str());
			return true;
		}
	}
	return true;
}
void XComParser::ParseTemplate()
{
	if (stackKey_.empty()) {
		return;
	}
	if (mapXComNodes_.find(stackKey_[0]) == mapXComNodes_.end()) {
		mapXComNodes_[stackKey_[0]] = std::make_shared<XComNode>();
	}
	std::shared_ptr<XComNode> xcomNode = mapXComNodes_[stackKey_[0]];
	std::string temp;

	for (auto &str : stackKVs_) {
		temp += str + "\n";
	}
	xcomNode->SetTemplate(temp);
	stackKVs_.clear();
}
std::shared_ptr<XComNode> XComParser::GetXComNode(const std::string &id)
{
	if (mapXComNodes_.find(id) == mapXComNodes_.end()) {
		return nullptr;
	}
	return mapXComNodes_[id];
}
std::string XComParser::GetCorrespStr(
	const std::string &line, int32_t &pos, const std::vector<char> &corr)
{
	std::stack<char> stackCh;
	stackCh.push(corr[0]);
	for (int i = pos + 1; i < line.length(); i++) {
		char ch = line[i];
		if (ch == corr[0]) {
			stackCh.push(corr[0]);
		} else if (ch == corr[1]) {
			stackCh.pop();
			if (stackCh.empty()) {
				std::string crsp = line.substr(pos, i - pos + 1);
				pos = i;
				return crsp;
			}
		}
	}
	pos = line.length();
	return line.substr(pos, line.length() - pos);
}
} // namespace protocol